package com.moonshineproject.plugin.actionscript.flashproject
{
	import com.moonshineproject.plugin.console.ConsoleOutputter;
	import com.moonshineproject.plugin.console.MarkupTextLineModel;
	import com.moonshineproject.text.TextLineModel;
	import com.moonshineproject.utils.HtmlFormatter;
	
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.Timer;
	
	// TODO: Move parsing / outputting to another file?
	public class MacFlashIDELaunchCommand extends ConsoleOutputter
	{	
		private var errorTimer:Timer;
		private var path:File;
		
		override public function get name():String { return "Flash IDE: " };
		
		public function buildFla(path:File):void
		{
			this.path = path;
			
			if (errorTimer && errorTimer.running) errorCheckTimeout(null);
			
			errorTimer = new Timer(500, 50); // Wait 25 seconds tops
			errorTimer.addEventListener(TimerEvent.TIMER, checkForErrors);
			errorTimer.addEventListener(TimerEvent.TIMER_COMPLETE, errorCheckTimeout);
			
			// Build script to open the .fla & run it (testMovie)
			var cmd:String = buildCommand(path);
			
			
			run(cmd, path);
		}
		
		private function buildCommand(path:File):String
		{
			// Open document, clear old errors, testMovive, write errors to file
			var cmd:String = 'var projectPath = "file://' + path.parent.nativePath + '/";\n';
			cmd += 'var target = "' + path.name + '";\n';
			cmd += 'var doc = fl.openDocument(projectPath+target);\n';
			cmd += 'fl.compilerErrors.clear();\n';
			cmd += 'doc.testMovie();\n';
			cmd += 'var errorURI = projectPath + doc.name + ".flashCompilerErrors";\n';
			cmd += 'fl.compilerErrors.save(errorURI);\n'
			
			return cmd;
		}
		
		private function run(cmd:String, path:File):void
		{
			// Move to the fla's dir
			var dir:File = path.parent; 
			
			// Create 'temp' file
			var file:File = new File(dir.nativePath + File.separator + path.name.split('.')[0] + "_launcher.jsfl");
			
			// Write cmd to it
			var fw:FileStream = new FileStream();
			fw.open(file, FileMode.WRITE);
			fw.writeUTFBytes(cmd);
			fw.close();
			
			// Remove default error output file
			var errFile:File = new File(dir.nativePath + File.separator + path.name + ".flashCompilerErrors");
			if (errFile.exists) errFile.deleteFile();
			
			try 
			{			
				file.openWithDefaultApplication();
				
				if (!errorTimer.running) errorTimer.start();
			}
			catch (e:Error)
			{
				// No Flash IDE.
				errorTimer.stop();
			}
		}
		
		private function checkForErrors(event:TimerEvent):void
		{
			var errFile:File = path.parent.resolvePath(path.name+".flashCompilerErrors");
			if (errFile.exists)
			{
				errorTimer.stop();
				errorTimer.removeEventListener(TimerEvent.TIMER, checkForErrors);
				
				parseErrors(errFile);
				
				errFile.deleteFile();
			}
		}
		
		private function errorCheckTimeout(event:TimerEvent):void
		{
			errorTimer.removeEventListener(TimerEvent.TIMER, checkForErrors);
			errorTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, errorCheckTimeout);	
		}
		
		private function parseErrors(errFile:File):void
		{
			var fs:FileStream = new FileStream();
			fs.open(errFile, FileMode.READ);
			var resultStr:String = fs.readMultiByte(fs.bytesAvailable, 'utf-8');
			fs.close();
			 
			var errorFinder:RegExp = /(\d+) Error/g;
			var warningFinder:RegExp = /(\d+) Warning/g;
			
			var numErrorsMatch:Object = errorFinder.exec(resultStr);
			var numWarningsMatch:Object = warningFinder.exec(resultStr);
			
			var numErrors:int = (numErrorsMatch) ? parseInt(numErrorsMatch[1]) : 99;
			var numWarnings:int = (numWarningsMatch) ? parseInt(numWarningsMatch[1]) : 99;
			
			if (numErrorsMatch && numErrors == 0 && numWarnings == 0)
			{
				print("Done.");
				return;
			}
			else if (resultStr != "")
			{
				var e:Array = findErrors(resultStr);
				printErrors(e);
				// TODO: Also parse warnings
			}
			else
			{
				print("Failed to parse output.");
				print("%s", resultStr);
			}
		}
		
		private function findErrors(str:String):Array
		{
			var match:Object;
			var errors:Array = [];
			
			// TODO: Get 'em in the correct order
			
			//**Error** /Users/erikpettersson/Desktop/TestingDir/TestyClass.as, Line 9	1046: Type was not found or was not a compile-time constant: Bar.
			var re:RegExp = /\*\*Error\*\* ([[\/\\].*?]*?\w*?\.\w+), Line (\d+)\s(.*?)$/gsm;
			while (match = re.exec(str))
			{
				errors.push({kind:'file', path:match[1], line:match[2], error:match[3]});
			}
			
			//**Error** Scene 1, Layer 'Layer 1', Frame 1, Line 2	1120: Access of undefined property bar.
			var re2:RegExp = /\*\*Error\*\* Scene \d+, Layer \'(.*?)\', Frame (\d+), Line (\d+)\s(.*?)$/gsm;
			while (match = re2.exec(str))
			{
				errors.push({kind:'timeline', layer:match[1], frame:match[2], line:match[3], error:match[4]});
			}
			
			//**Error** Symbol 'MovieSymbol', Layer 'Layer 1', Frame 1, Line 1	1120: Access of undefined property bar.
			var re3:RegExp = /\*\*Error\*\* Symbol \'(\w*?)\', Layer \'(.*?)\', Frame (\d+), Line (\d+)\s(.*?)$/gsm;
			while (match = re3.exec(str))
			{
				errors.push({kind:'symbol', symbol:match[1], layer:match[2], frame:match[3], line:match[4], error:match[5]});
			}
			
			return errors;
		}
		
		private function printErrors(errors:Array):void
		{
			var lines:Vector.<TextLineModel> = Vector.<TextLineModel>([]);
			var pre:String = "<error> ⚡  </error>";
			
			for each (var err:Object in errors)
			{
				var errStr:String;
				if (err.kind == "file")
				{
					errStr = HtmlFormatter.sprintf("%s<weak>:</weak>%s \t %s\n",
													err.path, err.line, err.error);
				}
				else if (err.kind == "symbol")
				{
					errStr = HtmlFormatter.sprintf("%s<weak>:</weak>%s \t %s\n",
													err.symbol, err.line, err.error);
				}
				else if (err.kind == "timeline")
				{
					errStr = HtmlFormatter.sprintf("%s<weak>:</weak>%s \t %s\n",
													err.layer, err.line, err.error);
				}
						
				var lineModel:TextLineModel = new MarkupTextLineModel(pre+errStr);
				lines.push(lineModel);	
			}
			
			outputMsg(lines);
			
		}

	}
}